package com.jk1123.us.common.util;


import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author huyoufu <https://github.com/huyoufu>
 * @TIME 2021-09-17 00:23
 * @description
 */
public class RSAUtil {
    private final static String CRYPTO_METHOD = "RSA";
    private final static String CYPHER = "RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING";
    private final static String SIGNATURE="MD5withRSA";
    private final static int CRYPTO_BITS = 2048;
    private final static String PUBLIC_KEY_NAME="PUBLIC_KEY";
    private final static String PRIVATE_KEY_NAME="PRIVATE_KEY";


    public static Map<String,String> generate(){
        Map<String,String> result=new HashMap<>();

        KeyPair keyPair = getKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();

        String PRIVATE_KEY = new String(Base64.getEncoder().encode(privateKey.getEncoded()));
        result.put(PRIVATE_KEY_NAME,PRIVATE_KEY);

        String PUBLIC_KEY = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
        result.put(PUBLIC_KEY_NAME,PUBLIC_KEY);

        return result;
    }

    public static String sign(String clearText,String privateKey){
        try {
            KeyFactory keyFac = KeyFactory.getInstance(CRYPTO_METHOD);
            KeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey.trim()));
            PrivateKey key = keyFac.generatePrivate(keySpec);
            Signature signature = Signature.getInstance(SIGNATURE);
            signature.initSign(key);
            signature.update(clearText.getBytes("utf-8"));
            return new String(Base64.getEncoder().encode(signature.sign()));
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
    public static boolean check(String clearText,String sign,String publicKey){
        try {
            KeyFactory keyFac = KeyFactory.getInstance(CRYPTO_METHOD);
            KeySpec keySpec =new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey.trim()));
            PublicKey key = keyFac.generatePublic(keySpec);
            Signature signature = Signature.getInstance(SIGNATURE);
            signature.initVerify(key);
            signature.update(clearText.getBytes("utf-8"));

            return signature.verify(Base64.getDecoder().decode(sign));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }



    public static String encrypt(String clearText,String publicKey) {
        String encryptedBase64 = "";
        try {
            KeyFactory keyFac = KeyFactory.getInstance(CRYPTO_METHOD);
            KeySpec keySpec =new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey.trim()));
            Key key = keyFac.generatePublic(keySpec);
            final Cipher cipher = Cipher.getInstance(CYPHER);
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptedBytes = cipher.doFinal(clearText.getBytes("UTF-8"));
            encryptedBase64 = new String(Base64.getEncoder().encode(encryptedBytes));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return encryptedBase64.replaceAll("(\\r|\\n)", "");
    }

    public static String decrypt(String encryptedBase64,String privateKey) {
        String decryptedString = "";
        try {
            KeyFactory keyFac = KeyFactory.getInstance(CRYPTO_METHOD);
            KeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey.trim()));
            Key key = keyFac.generatePrivate(keySpec);
            final Cipher cipher = Cipher.getInstance(CYPHER);
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedBase64);
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            decryptedString = new String(decryptedBytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return decryptedString;
    }



    private static KeyPair getKeyPair() {
        KeyPair kp = null;
        try {
            KeyPairGenerator kpg = KeyPairGenerator.getInstance(CRYPTO_METHOD);
            kpg.initialize(CRYPTO_BITS);
            kp = kpg.generateKeyPair();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return kp;
    }

    public static void main(String[] args) {
        Map<String, String> map = generate();
        String pub=map.get(PUBLIC_KEY_NAME);
        String pri=map.get(PRIVATE_KEY_NAME);

        System.out.println("公钥:"+pub);
        System.out.println("私钥:"+pri);

        String encrypt = encrypt("123456", pub);

        System.out.println("使用公钥加密的结果:"+encrypt);


        String decrypt = decrypt(encrypt, pri);
        System.out.println("使用私钥解密结果:"+decrypt);




        //加签
        String sign = sign("username=张三&password=123", pri);

        //验签
        boolean check = check("username=张三&password=123", sign, pub);
        System.out.println("签名的验证结果是:"+check);


    }
}
